#region Directives
using System;
using System.IO;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Collections.Generic;

using VisualStudioSensor;
using TeamFoundationServerSensor;

using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.VersionControl.Common;
#endregion

/// <summary>
/// Web Service Endpoint for the <seealso href="CheckinEvent"/>
/// </summary>
/// <remarks>
/// To Register this Notification Endpoint you will need to use the <seealso href="http://blogs.msdn.com/psheill/archive/2006/02/01/522386.aspx">BisSubscribe.exe</seealso> tool. 
/// For more information about Team Foundation Server Notifications <seealso href="http://blogs.msdn.com/buckh/archive/2006/04/10/event_filtering.aspx"/>
/// </remarks>
[WebService(Namespace = "http://schemas.conchango.com/TeamFoundation/2005/06/Services/Notification/Extensibility", Description = "")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CheckInEndpoint : TeamFoundationServerSensor.EndpointBase {

    /// <summary>
    /// Endpoint required to accept incoming <see href="CheckinEvent"/> notifications
    /// </summary>
    /// <param name="eventXml">Serialized version of the <see href="CheckinEvent"/></param>
    /// <param name="tfsIdentityXml">Serialized version of <see href="TFSIdentity"/></param>
    /// <returns>True if method succeeded in reading data. False otherwise.</returns>
    [WebMethod]
    [SoapDocumentMethod("http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", RequestNamespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
    public override Boolean Notify(string eventXml, string tfsIdentityXml) {
        CheckinEvent checkInEvent = this.CreateInstance<CheckinEvent>(eventXml);
        TFSIdentity tfsIdentity = this.CreateInstance<TFSIdentity>(tfsIdentityXml);

        Boolean success = true;

        // Get an instance of the version control server.
        TeamFoundationServer tfs = Connections.getNewTFSConnection();
        VersionControlServer vcs = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

        // For each item in the commit.
        foreach (ClientArtifact commitItem in checkInEvent.Artifacts) {
            try {

                // Common commit data items.
                Dictionary<string, string> pMap_DevEvent = new Dictionary<string, string>();
                pMap_DevEvent.Add("log", checkInEvent.Comment);

                // Extra Useful information to send
                pMap_DevEvent.Add("Project", checkInEvent.TeamProject);
                pMap_DevEvent.Add("CreationDate", checkInEvent.CreationDate);
                pMap_DevEvent.Add("TimeZoneOffset", checkInEvent.TimeZoneOffset);
                pMap_DevEvent.Add("FileOwner", checkInEvent.Owner);
                pMap_DevEvent.Add("Subtype", commitItem.ArtifactType);

                // Set some default information
                string type = commitItem.ArtifactType;
                int linesAdded = -1;
                int linesDeleted = -1;
                string fileName = "Overview";
                string revision = checkInEvent.Number.ToString();

                // Determine what type of data is in the artifact
                if (commitItem.ChangeType != null) {

                    // Get the current item to extract additional info from.
                    Item itemSource = vcs.GetItem(commitItem.ServerItem, VersionSpec.ParseSingleSpec(commitItem.ItemRevision, Settings.Instance.getSetting("TFSUser")));

                    // Determine if any previous revisions exist
                    if (itemSource.ChangesetId <= 1) {
                        
                        // Use number of lines in file as the indicator for the
                        // number of lines added.
                        try {

                            // Open file for reading.
                            string tempPath = "~/App_code/temp.tmp";
                            itemSource.DownloadFile(Server.MapPath(tempPath));
                            StreamReader sr = File.OpenText(Server.MapPath(tempPath));

                            // Count number of lines
                            linesAdded = 0;
                            while (sr.ReadLine() != null) {
                                linesAdded++;
                            }

                            sr.Close();
                        }
                        catch (Exception e){
                            Logger.TheInstance.Log("Could not get file " 
                                + itemSource.ServerItem + " because " 
                                + e, LogLevel.Error);
                        }

                        // Cannot have deleted or changed lines for a file that
                        // was just created.
                        linesDeleted = 0;
                        pMap_DevEvent.Add("linesChanged", 0 + "");
                    }
                    else {

                        // Get the previous revision of the file
                        Item itemTarget = vcs.GetItem(commitItem.ServerItem, 
                            VersionSpec.ParseSingleSpec((int.Parse(commitItem.ItemRevision) - 1).ToString(), 
                            Settings.Instance.getSetting("TFSUser")));
                        
                        // Get the differences between the two items.
                        ItemDifference diffences = new ItemDifference(itemSource, itemTarget);
                        linesAdded = diffences.LinesAdded;
                        linesDeleted = diffences.LinesDeleted;
                        pMap_DevEvent.Add("linesChanged", diffences.LinesChanged.ToString());
                    }

                    // Additional info to extract from the file.
                    type = commitItem.ChangeType;
                    fileName = commitItem.ServerItem;
                    revision = commitItem.ItemRevision;
                    pMap_DevEvent.Add("ItemURL", commitItem.Url);
                }

                // Send event info to Hackystat server.
                SensorShellProxy.TheInstance.SendCommitEventData(type,
                    DateTime.Now.Ticks.ToString(), fileName, 
                    checkInEvent.Committer, revision,
                    linesAdded, linesDeleted,
                    pMap_DevEvent);
            }
            catch (Exception e) {
                Logger.TheInstance.Log("Error sending commit data. Data will be lost. " + e, LogLevel.Error);
                success = false;
            }
        }

        return success;
    }
}